use crate::{
    config::AliceConfig,
    function::{command::canvas2d::Canvas2dCommand, resource::resource_system::AliceResource},
    math::{Matrix4f, Vector2u},
    ui::UiOutput,
};

use super::{
    canvas_pipeline::CanvasPipeline, render_pipeline::RenderPipeline, render_target::RenderTarget,
    rhi::Rhi, CustomRenderPipeline, RenderPipelineInitInfo, ScreenDescriptorInfo, ui_pipeline::UiRenderPipeline,
};

pub struct AliceRender {
    /// 输出视图渲染管线
    render_output_pipeline: RenderPipeline,
    render_output_pipeline_view: Option<wgpu::TextureView>,
    /// 2d 渲染管线
    canvas_pipeline: CanvasPipeline,
    canvas_pipeline_view: wgpu::TextureView,

    /// ui 渲染管线
    ui_pipeline:UiRenderPipeline,
    ui_pipeline_view:wgpu::TextureView,

    before_d2_pipeline: Vec<(Box<dyn CustomRenderPipeline>, wgpu::TextureView)>,
    after_d2_pipeline: Vec<(Box<dyn CustomRenderPipeline>, wgpu::TextureView)>,
    groups: Vec<wgpu::BindGroup>,
    pub rhi: Rhi,
    ui_output: UiOutput,
    config:AliceConfig
}

impl AliceRender {
    pub fn new(rhi: Rhi, resource: AliceResource, config: &AliceConfig) -> Self {
        let pipeline_init_info = RenderPipelineInitInfo {
            rhi: rhi.clone(),
            resource,
        };

        let screen_size = config.get_screen_size();
        // canvas pipeline
        let (_, view, group) =
            RenderTarget::create_render_attachment_texture(rhi.clone(), screen_size);
        let (_, ui_view, ui_group) =
            RenderTarget::create_render_attachment_texture(rhi.clone(), screen_size);

        Self {
            render_output_pipeline: RenderPipeline::new(pipeline_init_info.clone()),
            render_output_pipeline_view: None,
            canvas_pipeline: CanvasPipeline::new(pipeline_init_info.clone()),
            canvas_pipeline_view: view,

            ui_pipeline:UiRenderPipeline::new(pipeline_init_info),
            ui_pipeline_view:ui_view,

            before_d2_pipeline: Vec::new(),
            after_d2_pipeline: Vec::new(),
            rhi,
            groups: vec![group,ui_group],
            ui_output: Default::default(),
            config:config.clone()
        }
    }

    pub fn set_custom_output_render(&mut self, custom: wgpu::TextureView) {
        self.render_output_pipeline_view = Some(custom);
    }

    pub fn add_pipeline(
        &mut self,
        mut pipeline: Box<dyn CustomRenderPipeline>,
    ) -> &Box<dyn CustomRenderPipeline> {
        let order = pipeline.attach(self.rhi.clone());
        let screen_size = self.config.get_screen_size();
        match order {
            super::Order::BeforeD2 => {
                let (_, view, group) = RenderTarget::create_render_attachment_texture(
                    self.rhi.clone(),
                    screen_size,
                );
                self.before_d2_pipeline.push((pipeline, view));
                self.groups.push(group);
                return &self.before_d2_pipeline.last().unwrap().0;
            }
            super::Order::AfterD2 => {
                let (_, view, group) = RenderTarget::create_render_attachment_texture(
                    self.rhi.clone(),
                    screen_size,
                );
                self.after_d2_pipeline.push((pipeline, view));
                self.groups.push(group);
                return &self.after_d2_pipeline.last().unwrap().0;
            }
        }
    }

    pub fn resize(&mut self) {
        let screen_size = self.config.get_screen_size();
        let window_size = self.config.get_window_size();
        self.rhi.resize(window_size);
        self.resize_update_view(screen_size);
    }



    pub fn begin_frame(&mut self) {
        self.canvas_pipeline.begin_frame();
    }

    pub fn resize_update_view(&mut self, size: Vector2u) {
        // canvas pipeline
        let (_,view,group) = RenderTarget::create_render_attachment_texture(self.rhi.clone(),size);
        self.canvas_pipeline_view = view;
        self.groups[0] = group;

        let (_,view,group) = RenderTarget::create_render_attachment_texture(self.rhi.clone(),size);
        self.ui_pipeline_view = view;
        self.groups[1] = group;

        // TODO update custom pipeline view size
    }

    pub fn update_scene_camera(&mut self, camera: Matrix4f) {
        self.canvas_pipeline.update_scene_camera(camera)
    }

    pub fn tick(&mut self) {

        let screen_size = self.config.get_screen_size();
        let screen_descriptor = ScreenDescriptorInfo {
            view_port_size: screen_size,
            scale_factor: 1.0,
        };

        self.before_d2_pipeline
            .iter_mut()
            .for_each(|(p, v)| p.draw(&v, None, &screen_descriptor));
        self.canvas_pipeline.draw(&self.canvas_pipeline_view);
        self.after_d2_pipeline
            .iter_mut()
            .for_each(|(p, v)| p.draw(&v, None, &screen_descriptor));
        {
            self.ui_pipeline.draw(&self.ui_pipeline_view,&self.ui_output,&screen_descriptor);

        }
        //自定义输出管线 ， 要 处理 surface.get_current_texture() 
        if let Some(view) = &self.render_output_pipeline_view {
            self.render_output_pipeline
                .draw(view, Some(&self.groups[..]));
        } else {
            let output = self.rhi.borrow().surface.get_current_texture().unwrap();
            let view = output
                .texture
                .create_view(&wgpu::TextureViewDescriptor::default());

            self.render_output_pipeline
                .draw(&view, Some(&self.groups[..]));

            output.present();
        }
    }

    pub fn update_ui_output(&mut self, ui: UiOutput) {
        self.ui_output = ui;
    }

    pub fn submit_canvas_draw_command(&mut self, commands: Vec<Canvas2dCommand>) {
        let canvas = &mut self.canvas_pipeline;
        for cmd in commands {
            match cmd {
                Canvas2dCommand::QuadWithPSF(cmd) => {
                    canvas.draw_quad_with_psf(
                        cmd.position,
                        cmd.size,
                        cmd.texture,
                        cmd.factory,
                        cmd.color,
                    );
                }
                Canvas2dCommand::QuadWithPST(cmd) => canvas.draw_quad_with_pst(
                    cmd.position,
                    cmd.size,
                    cmd.texture,
                    cmd.factory,
                    cmd.color,
                ),
                Canvas2dCommand::QuadWithTC(cmd) => {
                    canvas.draw_quad_with_tc(cmd.transform, cmd.color)
                }
                Canvas2dCommand::QuadWithTFC(cmd) => {
                    canvas.draw_quad_with_tfc(cmd.transform, cmd.texture, cmd.factory, cmd.color)
                }
            }
        }
    }
}
